#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.ComponentModel;
using System.Reflection;
using NUnit.Framework;
using Newtonsoft.Utilities.Miscellaneous;
using System.Threading;
using Newtonsoft.Utilities.Testing;

namespace Newtonsoft.Utilities.Tests.Miscellaneous
{
  public class EventUtilsTests : TestsBase
  {
    public event HandledEventHandler RaiseTestEvent;
    public event EventHandler<UnhandledExceptionEventArgs> RaiseGenericEventHandlerEvent;
    public event ResolveEventHandler ReturnTestEvent;
    public event ResolveEventHandler RaiseAsyncTestEvent;
    public event ResolveEventHandler RaiseCallbackAsyncTestEvent;
    public event ResolveEventHandler RaiseCallbackAsyncReturnValue;
    public event ResolveEventHandler RaiseAsyncExceptionEvent;

    [Test]
    public void Raise()
    {
      bool flag = false;
      RaiseTestEvent += delegate(object o, HandledEventArgs e)
                        {
                          flag = e.Handled;
                        };


      RaiseTestEvent.Raise(delegate(HandledEventHandler e) { e(this, new HandledEventArgs(true)); });
      Assert.AreEqual(true, flag);
    }

    [Test]
    public void RaiseGenericEventHandler()
    {
      bool isTerminating = false;
      Exception exception = null;

      RaiseGenericEventHandlerEvent += delegate(object o, UnhandledExceptionEventArgs e)
                                       {
                                         isTerminating = e.IsTerminating;
                                         exception = (Exception)e.ExceptionObject;
                                       };

      RaiseGenericEventHandlerEvent.Raise(this, new UnhandledExceptionEventArgs(new Exception("Test"), true));
      Assert.AreEqual(true, isTerminating);
      Assert.IsNotNull(exception);
      Assert.AreEqual("Test", exception.Message);
    }

    [Test]
    public void RaiseWithReturn()
    {
      string name = null;
      ReturnTestEvent += delegate(object o, ResolveEventArgs e)
                         {
                           name = e.Name;
                           return Assembly.GetExecutingAssembly();
                         };

      Assembly result = ReturnTestEvent.Raise(delegate(ResolveEventHandler e) { return e(this, new ResolveEventArgs("Pie")); });
      Assert.AreEqual("Pie", name);
      Assert.AreEqual(Assembly.GetExecutingAssembly(), result);
    }

    [Test]
    public void RaiseAsync()
    {
      string result = null;
      AutoResetEvent asyncFlag1 = new AutoResetEvent(false);
      AutoResetEvent asyncFlag2 = new AutoResetEvent(false);

      RaiseAsyncTestEvent += delegate(object o, ResolveEventArgs e)
                             {
                               result = "Start";
                               Assert.AreEqual("Start", result);

                               // let test continue and wait until Assert complete
                               asyncFlag1.Set();
                               asyncFlag2.WaitOne();

                               result = e.Name;
                               Assert.AreEqual("ResolveEventArgs", result);

                               // let test continue
                               asyncFlag1.Set();

                               return null;
                             };

      RaiseAsyncTestEvent.RaiseAsync(delegate(ResolveEventHandler e) { e(this, new ResolveEventArgs("ResolveEventArgs")); });

      // wait until async method has started
      asyncFlag1.WaitOne();

      Assert.AreEqual("Start", result);

      // let async method continue and wait until setting result complete
      asyncFlag2.Set();
      asyncFlag1.WaitOne();

      Assert.AreEqual("ResolveEventArgs", result);
    }

    [Test]
    public void RaiseAsyncLoad()
    {
      int totalRunCount = 1000;
      int currentRunCount = 0;
      AutoResetEvent asyncFlag1 = new AutoResetEvent(false);

      for (int i = 0; i < totalRunCount; i++)
      {
        Action<int> a = delegate(int v)
                                           {
                                             Interlocked.Increment(ref currentRunCount);
                                             asyncFlag1.Set();
                                           };
        a.RaiseAsync(delegate(Action<int> e) { e(i); });
      }

      while (true)
      {
        asyncFlag1.WaitOne();

        if (currentRunCount == totalRunCount)
          break;
      }
    }

    [Test]
    public void RaiseAsyncCallback()
    {
      string name = null;
      AutoResetEvent asyncFlag1 = new AutoResetEvent(false);
      AutoResetEvent asyncFlag2 = new AutoResetEvent(false);

      RaiseCallbackAsyncTestEvent += delegate(object o, ResolveEventArgs e)
                                     {
                                       name = "Start";
                                       Assert.AreEqual("Start", name);

                                       // let test continue and wait until Assert complete
                                       asyncFlag1.Set();
                                       asyncFlag2.WaitOne();

                                       name = e.Name;
                                       Assert.AreEqual("ResolveEventArgs", name);

                                       // let test continue
                                       asyncFlag1.Set();

                                       return null;
                                     };

      EventCallBack callback = delegate
                               {
                                 asyncFlag2.WaitOne();

                                 name = "Callback";

                                 // let test continue
                                 asyncFlag1.Set();
                               };

      RaiseCallbackAsyncTestEvent.RaiseAsync(delegate(ResolveEventHandler e) { e(this, new ResolveEventArgs("ResolveEventArgs")); }, callback);

      // wait until async method has started
      asyncFlag1.WaitOne();

      Assert.AreEqual("Start", name);

      // let async method continue and wait until setting result complete
      asyncFlag2.Set();
      asyncFlag1.WaitOne();

      Assert.AreEqual("ResolveEventArgs", name);

      // wait until callback has started
      asyncFlag2.Set();
      asyncFlag1.WaitOne();

      Assert.AreEqual("Callback", name);
    }

    [Test]
    public void RaiseAsyncCallbackReturnValue()
    {
      string name = null;
      AutoResetEvent asyncFlag1 = new AutoResetEvent(false);
      AutoResetEvent asyncFlag2 = new AutoResetEvent(false);

      RaiseCallbackAsyncReturnValue += delegate(object o, ResolveEventArgs e)
                                       {
                                         name = "Start";
                                         Assert.AreEqual("Start", name);

                                         // let test continue and wait until Assert complete
                                         asyncFlag1.Set();
                                         asyncFlag2.WaitOne();

                                         name = e.Name;
                                         Assert.AreEqual("ResolveEventArgs", name);

                                         // let test continue
                                         asyncFlag1.Set();

                                         return Assembly.GetExecutingAssembly();
                                       };

      EventCallBack<Assembly> callback = delegate(EventResult<Assembly> result)
                                         {
                                           asyncFlag2.WaitOne();

                                           Assert.AreEqual(Assembly.GetExecutingAssembly(), result.ReturnValue);

                                           name = "Callback";

                                           // let test continue
                                           asyncFlag1.Set();
                                         };

      RaiseCallbackAsyncReturnValue.RaiseAsync(delegate(ResolveEventHandler e) { return e(this, new ResolveEventArgs("ResolveEventArgs")); }, callback);

      // wait until async method has started
      asyncFlag1.WaitOne();

      Assert.AreEqual("Start", name);

      // let async method continue and wait until setting result complete
      asyncFlag2.Set();
      asyncFlag1.WaitOne();

      Assert.AreEqual("ResolveEventArgs", name);

      // wait until callback has started
      asyncFlag2.Set();
      asyncFlag1.WaitOne();

      Assert.AreEqual("Callback", name);
    }

    [Test]
    public void RaiseAsyncException()
    {
      AutoResetEvent asyncFlag1 = new AutoResetEvent(false);

      RaiseAsyncExceptionEvent += delegate
                                  {
                                    throw new Exception("AsyncException");
                                  };

      EventCallBack callback = delegate(EventResult result)
                               {
                                 Assert.IsNotNull(result);
                                 Assert.IsNotNull(result.EventException);
                                 Assert.IsInstanceOfType(typeof(Exception), result.EventException);
                                 Assert.AreEqual("AsyncException", result.EventException.Message);

                                 asyncFlag1.Set();
                               };

      RaiseAsyncExceptionEvent.RaiseAsync(delegate(ResolveEventHandler e) { e(this, new ResolveEventArgs("ResolveEventArgs")); }, callback);

      // wait until async callback Asserts have completed
      asyncFlag1.WaitOne();
    }
  }
}
